package com.zs.java.generic;

/**
 * 泛型有三种使用方式，分别为：泛型类、泛型接口、泛型方法
 * 此处T可以随便写为任意标识，常见的如T、E、K、V等形式的参数常用于表示泛型
 * 在实例化泛型类时，必须指定T的具体类型
 *
 * @author madison
 * @description
 * @date 2021/6/1 12:24
 */
public class Generic<T> {
    //key这个成员变量的类型为T,T的类型由外部指定
    private T key;

    //泛型构造方法形参key的类型也为T，T的类型由外部指定
    public Generic(T key) {
        this.key = key;
    }

    //泛型方法getKey的返回值类型为T，T的类型由外部指定
    public T getKey() {
        return key;
    }

    /**
     * 泛型方法的基本介绍
     *
     * @param tClass 传入的泛型实参
     * @return T 返回值为T类型
     * 说明：
     * 1）public 与 返回值中间<T>非常重要，可以理解为声明此方法为泛型方法。
     * 2）只有声明了<T>的方法才是泛型方法，泛型类中的使用了泛型的成员方法并不是泛型方法。
     * 3）<T>表明该方法将使用泛型类型T，此时才可以在方法中使用泛型类型T。
     * 4）与泛型类的定义一样，此处T可以随便写为任意标识，常见的如T、E、K、V等形式的参数常用于表示泛型。
     */
    public static <T> T genericMethod(Class<T> tClass) throws Exception {
        T instance = tClass.getDeclaredConstructor().newInstance();
        return instance;
    }

    /**
     * 这才是一个真正的泛型方法。
     * 首先在public与返回值之间的<E>必不可少，这表明这是一个泛型方法，并且声明了一个泛型E
     * 这个E可以出现在这个泛型方法的任意位置.
     * 泛型的数量也可以为任意多个
     * 如：public <E,K> K showKeyName(Generic<E> container){
     * ...
     * }
     */
    public <E> E showKeyName(Generic<E> container) {
        System.out.println("container key :" + container.getKey());
        //当然这个例子举的不太合适，只是为了说明泛型方法的特性。
        E test = container.getKey();
        return test;
    }

    public <T> void printMsg(T... args) {
        for (T t : args) {
            System.out.println("泛型测试 t is " + t);
        }
    }

    public static void showKeyValue1(Generic<Number> obj) {
        System.out.println("泛型测试key1 value is " + obj.getKey());
    }

    public static void showKeyValue2(Generic<?> obj) {
        System.out.println("泛型测试key2 value is " + obj.getKey());
    }

    /**
     * 泛型上下边界
     *
     * @param obj
     */
    public void showKeyValue3(Generic<? extends Number> obj) {
        System.out.println("泛型测试 key value is " + obj.getKey());
    }

    public static void main(String[] args) throws Exception {
        //泛型的类型参数只能是类类型（包括自定义类），不能是简单类型
        //传入的实参类型需与泛型的类型参数类型相同，即为Integer.
        Generic<Integer> integerGeneric = new Generic<>(12345);
        System.out.println(integerGeneric.getKey());

        //定义的泛型类，就一定要传入泛型类型实参么？并不是这样
        Generic generic = new Generic("1111");
        Generic generic1 = new Generic(4444);
        Generic generic2 = new Generic(55.55);
        Generic generic3 = new Generic(true);
        System.out.println(generic.getKey());
        System.out.println(generic1.getKey());
        System.out.println(generic2.getKey());
        System.out.println(generic3.getKey());

        //泛型通配符
        Generic<Integer> gInteger = new Generic<>(22223);
        Generic<Number> gNumber = new Generic<>(55555);
        showKeyValue1(gNumber);
//        showKeyValue1(gInteger);

        showKeyValue2(gNumber);
        showKeyValue2(gInteger);

        Object o = genericMethod(Class.forName("com.zs.java.generic.Main"));
        System.out.println(o);

        integerGeneric.printMsg("111", 222, "aaaa", "2323.4", 55.55, false);


        Generic<String> generic4 = new Generic<String>("11111");
        Generic<Integer> generic5 = new Generic<Integer>(2222);
        Generic<Float> generic6 = new Generic<Float>(2.4f);
        Generic<Double> generic7 = new Generic<Double>(2.56);
        // 这一行代码编译器会提示错误，因为String类型并不是Number类型的子类
        // generic4.showKeyValue3(generic4);
        generic4.showKeyValue3(generic5);
        generic4.showKeyValue3(generic6);
        generic4.showKeyValue3(generic7);
    }
}
